<?php

/**
*Form Builder & Admin Generator

*@author marcio <opi14@op.pl>, <polishvodka7@gmail.com>
*@copyright Copyright (c) 2011, marcio
*@version 1.6.5
*/

require_once('libraries/Db.php');
require_once('libraries/Loader.php');
require_once('libraries/Validator.php');
require_once('libraries/Language.php');
require_once('libraries/Form.php');
require_once('libraries/Exception.php');

class Volta_Form_Builider {

	/**
	*Skladowa klasy za pomoca ktorej wiadomo czy opcja edycji zostala wlaczona lub nie
	*@var array $db_fields_equivalent
	*@access protected
	*/
	protected $edit;
	
	/**
	*Skladowa klasy ktora przechowywuje tablice elemntow ktore odpowiadaja kolumna z bazy -> typ danych w html
	*@var array $db_fields_equivalent
	*@access protected
	*/
	protected $db_fields_equivalent = array(
	
                                          'username' => 'text',	
										  'user' => 'text',	
										  'login' => 'text',	
										  'uzytkownik' => 'text',	
										  'autor' => 'text',
										  'author' => 'text',
										  'tytul' => 'text',
										  'title' => 'text',
										  'password' => 'password',	
										  'pwd' => 'password',	
										  'pass' => 'password',	
										  'passwd' => 'password',
										  'haslo' => 'password',	
										  'tresc' => 'textarea',
										  'content' => 'textarea',
										  'file' => 'file',	
										  'uploaded_file' => 'file',
										  'data' => 'hidden',
										  'id' => 'hidden',
										  'news_id' => 'hidden',
										  'token' => 'hidden'
										  

	                                      );
	/**
	*Skladowa klasy ktora przechowywuje reguly walidacji dla danej kolumny z bazy
	*@var array $db_fields_rules
	*@access protected
	*/
	protected $db_fields_rules = array(	
	                                  );
									  
	/**
	*Skladowa klasy ktora przechowywuje tablice elemntow ktore odpowiadaja atrybutowi action="" formularza (odpowiedniki kolumn z bazy)
	*@var array $column_to_form_action
	*@access protected
	*/
	protected $column_to_form_action = array(
	
										'id',
										'tid',
										'pid'
										
										 );
						
	/**
	*Skladowa klasy gdy true atrybut action="" formularza buduje sie na podstawie nice url w przeciwnym wypadnu zwykly get
	*@var array $db_fields_equivalent
	*@access protected
	*/
	protected $nice_url = false;
	

	/**
	*Skladowa klasy ktora przechowuje separator kiedy uzywamy przyjazne url'e
	*@var string $params_separator
	*@access protected
	*/
	protected $params_separator = ',';

	/**
	*Skladowa klasy ktora przechowuje atrubut action="" dla budowy formularza
	*@var string $form_action
	*@access protected
	*/
	protected $form_action = '';

	/**
	*Skladowa klasy ktora przechowywuje pola z bazy danych
	*@var array $fields
	*@access protected
	*/										
	protected $fields = array();
	
	/**
	*Skladowa klasy ktora przechowywuje atrubuty danego pola
	*@var array $fields_attr
	*@access protected
	*/		
	protected $fields_attr = array();
	
	/**
	*skladowa ktora przechowywuje instancje klasy do walidacji formularzy
	*@var object $validation
	*@access private
	*/	
	private $validation;
	
	/**
	*skladowa ktora przechowywuje instancje klasy do obslugi bazy danych
	*@var object $validation
	*@access private
	*/	
	private $db;
	
	/**
	*ustawiamy czy formularz ma zostac wyswietlony czy zwrocony w postaci tablicy by moc ulozyc elementy jak sie chce
	*@var bool $render
	*@access public
	*/	
	public $render = true;
	

	/**
	*Kontruktor ustawia nam wszystkie pola do wypelnienia formularza
	*@access public 
	*@param array $fields tablica z danymi pobranymi z bazy np: select * from news where id = 1 limit 1
	*/
	public function __construct($fields, $edit = false) 
	{	
		$this -> validation = new Volta_Validator();
		$this -> db = Db::Factory("MySql");
		$this -> edit = $edit;
	
		if(is_array($fields)) 
		{
			$this -> fields = $fields;	
		}
		
		else
			throw new InvalidArgumentException('Podany argument do konstruktora nie jest tablica');
	}
	
	/**
	*Zwraca nam podane pole
	*@access public 
	*@param string $key nazwa pola
	*@return mixed $this -> fileds[0][$key] zwraca nam zawartosc pola
	*/
	public function __get($key) 
	{
		if(array_key_exists($key, $this -> fields[0]))
			return $this -> fields[0][$key];
		//else
			//throw new Volta_Form_Builider_Exception("Blad podczas pobierania zawartosci nie znaleziono kolumny {$key}");
	}
	
	/**
	*Ustawia $value na pole $key
	*@access public 
	*@param string $key nazwa pola
	*@param string $value wartosc pola
	*/
	public function __set($key, $value) 
	{
		if(array_key_exists($key, $this -> fields[0]))
			$this -> fields[0][$key] = $value;
		else
			throw new Volta_Form_Builider_Exception("Odwolywanie sie do nieistniejacej kolumny {$key} nie powiodlo sie");
	}
	
	/**
	*Ustawia atrubuty dla pola model funkjci: set_nazwaPola_atrybut('wartosc'); lun get_nazwaPola_atrybut()
	*dla textarea tag value musi byc ustaiony jako ostatni
	*@access public 
	*@param string $name nazwa metody
	*@param string|array parametry metody
	*@return null|string
	*/
	public function __call($name, $args)
	{
	
		$explode = explode('_', $name);
		
		if(sizeof($explode) == 3)
		{
			$type = $explode[0];
			$field = $explode[1];
			$attribute = $explode[2];

			if($type == 'set')
			{
				if(!in_array('name', (array)$this -> fields_attr[$field]))
					$this -> fields_attr[$field]['name'] = $field;
					
				$this -> fields_attr[$field][$attribute] = $args[0];
			}
			
			else if($type == 'get')
				return $this -> fields_attr[$field][$attribute];
			
			else
				throw new Volta_Form_Builider_Exception("Odwolywanie sie do nieistniejacej metody {$name}");
		}
		
		else if(sizeof($explode) < 3 || sizeof($explode) > 3)
			throw new Volta_Form_Builider_Exception("Odwolywanie sie do nieistniejacej metody {$name}");
	}
	
	/**
	*Jesli wyprintujemy obiekt wyswietl nam tablice danych z bazy danych
	*@access public 
	*@return array $this -> fields
	*/
	public function __toString()
	{
		return $this -> debug();
	}
	
	/**
	*Ladujemy potrzebne nam walidatory
	*@access public 
	*@param string|array $validators
	*/
	public function load_validators($validators)
	{
		$this -> validation -> load($validators);
	}
	
	/**
	*Dodaje pole do formularza
	*@access public 
	*@param string $field nazwa pola
	*@param string| $type typ pola
	*/
	public function add_field($field) 
	{
		if(!array_key_exists($field, $this -> fields[0]))
			$this -> fields[0][$field] = null;
	}
	
	/**
	*Dodaje wartosci dla danej kolumny pobranej z bazy ktora odpowiada odpowiedniemu tagu w html
	*@access public 
	*@param string $field nazwa pola
	*@param string|array $values wartosci ktore maja byc dostepne dla danego pola
	*/
	public function add_values($field, $values) 
	{
		$this -> fields[0][$field] = array_merge((array)$this -> fields[0][$field], (array)$values);
	}
	
	/**
	*Czysci zawartosc danego pola z tablicy $this -> fields
	*@access public 
	*@param string $field nazwa pola
	*/
	public function clear_values($field) 
	{
		$this -> fields[0][$field] = array();
	}
	
	
	/**
	*Zwraca nam zawartosc danego pola
	*@access public 
	*@param string $field nazwa pola
	*/
	public function get_field($field) 
	{
		if(array_key_exists($field, $this -> fields[0])) 
			return $this -> fields[0][$field];
	
		return null;	
	}
	
	/**
	*Dodaje wartosci dla danej kolumny pobranej z bazy ktora odpowiada odpowiedniemu tagu w html
	*@access public 
	*@return array|null $vars zwraca nam tablice gdy posiada conajmniej jeden element w przeciwnym wypadku null
	*/
	public function get_columns()
	{
		if(sizeof($this -> fields[0]) > 0)
			foreach($this -> fields[0] as $key => $value)
				$vars[] = $key;
			
		return (sizeof($vars > 0)) ? $vars : null;
	}
	
	/**
	*Zwraca nam $this -> fields za pomoca print_r z 2 arg. na true
	*@access public 
	*@return array
	*/
	public function debug() 
	{
		return '<pre>'.print_r($this -> fields, true).'</pre>';	
	}
	
	/**
	*Zwraca typ html dla danej kolumny
	*@access public 
	*@param string $name nazwa kolumny
	*@return string|null typ html
	*/
	public function get_equivalent_type($name) 
	{	
		if(is_array($name))
		{
			$key = array_keys($this -> fields_attr, $name);
			return $this -> db_fields_equivalent[$key[0]];
		}
	
		else if(array_key_exists($name, $this -> db_fields_equivalent))
			return $this -> db_fields_equivalent[$name];
			
		return null;
	}
	
	/**
	*Ustawia nam dla danej kolumny odpowiedni typ w html
	*@access public 
	*@param string $key nazwa pola
	*@param string $type
	*przyklad uzycia np kolumna news_author w $this -> db_fields_equivalent jest typu text chcemy ja zmienic na textarea
	*
	*    $instance -> set_equivalent('news_author', 'textarea');
	*
	*/
	public function set_equivalent($field, $type) 
	{
		if(array_key_exists($field, $this -> db_fields_equivalent))
			$this -> db_fields_equivalent[$field] = $type;
		else
			throw new Volta_Form_Builider_Exception("Brak odpowiedniej kolumny");
	}
	
	/**
	*Dodaje na poczatku zapytania http '?' lub '/' w zaleznosci czy rewrite url sa on/off
	*@access protected
	*@return string zwraca prefix
	*/
	protected function get_http_query_prefix()
	{
		if(!$this -> nice_url)
			return '?';
		return '/';
	}
	
	/**
	*Ustawia separator ktory lacze automatycznie tworzony action z tym uswtawianym recznie
	*@access protected
	*@param string $action
	*@return string $separator zwraca separator
	*/
	protected function get_separator($action)
	{
		if(!empty($action))
		{
			if(!$this -> nice_url)
				$separator = '&';
			else
				$separator = $this -> params_separator;
		}
		
		else
			$separator = '';
			
		return $separator;
	}
	
	/**
	*Ustawia action="" formularza na podstawie danych w $this -> column_to_form_action
	*@access protected
	*@return string action dla formularza
	*/
	protected function set_form_action()
	{
		$action_params = array();

		foreach($this -> column_to_form_action as $column_name)
		{
			if(array_key_exists($column_name, $this -> fields[0]))
			{
				if(!$this -> nice_url)
					$action_params[$column_name] = $this -> fields[0][$column_name];
				
				else
					$action .= $this -> fields[0][$column_name] . $this -> params_separator;
			}		
		}

		$build_query = $action . http_build_query($action_params, '', '&');
		return ($this -> nice_url) ? substr($action, 0, strlen($action) - 1) : $build_query;
	}
	
	/**
	*Dodaje akcje do formularza
	*@access public
	*uzywamy $instance -> add_form_action('add', 'news'); czyli akcja add() dla modulu news (nice urls)
	*lub $instance -> add_form_action(array('action' => 'add', 'module' => 'news'));
	*/
	public function add_form_action()
	{

		$args = func_num_args();
		$argv = func_get_args();
		
		if($args == 1 && is_array($argv[0]))
		{
			foreach($argv[0] as $get_var => $action_name)
			{
				if(!$this -> nice_url)
					$get[$get_var] = $action_name;
				else
					$get .= $action_name . $this -> params_separator;
			}
			
			if(!$this -> nice_url)
				$get = http_build_query($get, '', '&');
			else
				$get = substr($get, 0, strlen($get) - 1);
				
		}
		
		else if($args > 1 && $this -> nice_url === true)
		{
			for($i = 0; $i <= sizeof($args); $i++)
				$get .= $argv[$i] . $this -> params_separator;
		}
		
		else
			$get = $argv[0];
		
		$this -> form_action = ($args > 1) ? substr($get, 0, strlen($get) - 1) : $get;
	}
	
	/**
	*Ustawiamy reguly walidacji ktore sa w konfiguracji klasy
	*@access public
	*/
	protected function set_rules()
	{
		foreach($this -> db_fields_rules as $key => $arr)
		{
			foreach($arr as $k => $rules)
			{
				foreach($rules as $validator => $rule)
				{
					if(array_key_exists($key, $this -> fields[0]))
					{
						$this -> load_validators($validator);
						$this -> add_rules($key, new $validator($rule));
					}
				}
			}
		}
	}
	
	/**
	*Dodaje lub zmienia regule dla danej kolumny
	*@access public
	*/
	public function add_rules($key, IValidation $validator)
	{
		if(!($validator instanceof IValidation))
			throw new InvalidArgumentException('Podany walidator nie posiada interfejsu IValidation');
		
		$this -> validation -> add_rule($key, $validator);
	}
	
	/**
	*Walidujemy formularz i w przypadku bledow zwracamy je
	*@access public
	*@return array|string|null
	*/
	public function validate()
	{
		$this -> validation -> add_data($_POST);
		$this -> set_rules();
		$this -> validation -> validation();
	 
		$errors = $this -> validation -> get_errors();
		
		if(sizeof($errors) >= 1)
			return $errors;
		return null;
	}
	
	/**
	*Po walidacji zapisujemy/edytujemy dane z formularza do bazy danych
	*@access public
	*@param string $table tabela do ktorej chcemy zapisac/edytowac dane
	*@param array $where warunek where jesli edytujemy
	*@param array $skip zmienne post ktore nie maja byc zapisane w bazie
	*@return bool
	*/
	//dla update
	//$this -> instance -> save('news', array('id' => 1));
	public function save($table, $where = array(), $skip = array())
	{
		foreach($this -> fields[0] as $column => $record)
		{
			if(sizeof($skip) > 0)
				if(!in_array($column, $skip))
					$data[$column] = $_POST[$column];
				else
					continue;
		}
	
		if(!$this -> edit)
		{			
			if($this -> db -> Insert($table, $data))
				return true;
			return false;
		}
		
		else
		{
			if($this -> db -> Update($table, $data, $where))
				return true;
			return false;
		}
	}
		
	/**
	*Tworzy wszystkie pola na podstawie kolumn i jej typu
	*@access protected 
	*@param string $name typ tagu
	*@param string $value wartosc dla tagu html
	*@param bool $edit gdy true dodaje atrybut value z zawartoscia do kazdego tagu html
	*/
	protected function create_tags($name, $value, $edit = false) 
	{ 
	
		switch($this -> get_equivalent_type($name)) 
		{
			
				case 'text':
				            if($edit)
								Volta_Form::text($name, $value);
								
							else
								Volta_Form::text($name);

						    break;
							
				case 'hidden':
				              if($edit)
								  Volta_Form::hidden($name, $value);
								
							  else
								  Volta_Form::hidden($name);

						      break;
							
				case 'textarea':
								if($edit)
									Volta_Form::textarea($name, $value);
								
								else
									Volta_Form::textarea($name);

								break;
								
				case 'password':
								if($edit)
									Volta_Form::password($name, $value);
								
								else
									Volta_Form::password($name);
									
								break;
								
				case 'file':
							Volta_Form::input_file($name);
							break;
							
				case 'checkbox':
								if($edit) 
								{		
									if(is_string($this -> fields[0][$name]) && is_string($value))
									{
										if($this -> fields[0][$name] == $value)
											Volta_Form::checkbox($name, $value, true);
										else
											Volta_Form::checkbox($name, $value);
									}
									
									else if(is_array($this -> fields[0][$name]))
									{
										foreach($this -> fields[0][$name] as $key => $checkbox_value)
										{
											if($checkbox_value == $value[0])
												Volta_Form::checkbox($name, $checkbox_value, true);
											else
												Volta_Form::checkbox($name, $checkbox_value);
										}
									}	
								}	
								
								else
									Volta_Form::checkbox($name);

								break;
								
				case 'radio':
								if($edit) 
								{		
									if(is_string($this -> fields[0][$name]) && is_string($value))
									{
										if($this -> fields[0][$name] == $value)
											Volta_Form::radio($name, $value, true);
										else
											Volta_Form::radio($name, $value);
									}
									
									else if(is_array($this -> fields[0][$name]))
									{
										foreach($this -> fields[0][$name] as $key => $radio_value)
										{
											if($radio_value == $value[0])
												Volta_Form::radio($name, $radio_value, true);
											else
												Volta_Form::radio($name, $radio_value);
										}
									}	
								}	
								
								else
									Volta_Form::radio($name);

								break;
								
				case 'menu':
								if($edit)
									Volta_Form::menu($name, $value);
								
								else
									Volta_Form::menu($name);
									
								break;
								
				default: 
								$element = $this -> get_equivalent_type($name);
								
								if(file_exists('libraries/drivers/Element/'.$element.'.php'))
								{
									require_once('libraries/drivers/Element/'.$element.'.php');
								
									$attributes = (isset($this -> field_attr[$name])) ? $this -> field_attr[$name] : array($name);
								
									$element_obj = new $element();
									$element_obj -> set_label($name);
									$element_obj -> set_value($value);
									$element_obj -> set_attributes($attributes);
								
									Volta_Form::add_html_to_form($element_obj -> render());
								}
								
								break;
			
			}
	
	}
	
	/**
	*Tworzy caly formularz gotowy do wyswietlenia
	*@access public 
	*@param bool $edit gdy true ustawia value dla kazdego tagu
	*@return string|null gotowy formularz
	*/
	public function create_form() 
	{

		$prefix = $this -> get_http_query_prefix();
		$action = $this -> set_form_action();
		$separator = $this -> get_separator($action);	
		$act = $prefix . $this -> form_action . $separator . $action;

		if(!$this -> render)
			Volta_Form::open($act);
		else	
			Volta_Form::open_with_table($act);

		foreach($this -> fields as $field) 
		{		
			foreach($field as $name => $value) 
			{
				if(isset($this -> fields_attr[$name]))
					$this -> create_tags($this -> fields_attr[$name], $value, $this -> edit);
				
				else
					$this -> create_tags($name, $value, $this -> edit);
			}
		}
		
		Volta_Form::submit();
		
		if(!$this -> render)
			Volta_Form::close();
		else
			Volta_Form::close_with_table();
			
		return Volta_Form::get_form();
	}
	
} // end class

?>